fix: add boolean string coercion for FastMCP tool parameters#1848
fix: add boolean string coercion for FastMCP tool parameters#1848jayhemnani9910 wants to merge 1 commit intomodelcontextprotocol:mainfrom
Conversation
Some LLM clients incorrectly serialize booleans as strings ("false"
instead of false in JSON). The existing pre_parse_json method was
supposed to handle this via json.loads, but boolean values were being
skipped because bool is a subclass of int in Python, and the skip
condition isinstance(pre_parsed, str | int | float) matches booleans.
This fix adds explicit case-insensitive boolean string coercion before
the generic JSON parsing logic, properly converting "true"/"false"
strings to Python True/False when the parameter annotation is bool.
Fixes modelcontextprotocol#1843
There was a problem hiding this comment.
Pull request overview
This PR fixes an issue where LLM clients incorrectly serialize boolean values as strings (e.g., "false" instead of false in JSON), causing FastMCP tools to receive string values instead of Python booleans. This made if param: checks evaluate incorrectly since the string 'false' is truthy in Python.
Changes:
- Added explicit case-insensitive boolean string coercion in the
pre_parse_jsonmethod to convert"true"/"false"strings to PythonTrue/False - Added 6 comprehensive test cases covering lowercase, case-insensitive, native booleans, non-boolean strings, runtime validation, and type-specific behavior
- The fix is applied before generic JSON parsing to handle the edge case where
json.loads("false")returnsFalse, butisinstance(False, int)isTrue, causing the parsed boolean to be skipped
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/mcp/server/fastmcp/utilities/func_metadata.py |
Added boolean string coercion logic in pre_parse_json method to handle string "true"/"false" values for bool-annotated parameters |
tests/server/fastmcp/test_func_metadata.py |
Added 6 comprehensive test functions validating boolean coercion for various cases including case-insensitivity, native booleans, non-boolean strings, runtime validation, and type specificity |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Can I have a very simple MRE to understand what is wrong? |
I might be wrong🙃, please let me know accordingly. Why it broke:
|
|
You aren't supposed to call |
|
Thanks for the PR! We're still discussing whether this coercion should live in the SDK — see #1843. Going to close this for now and defer the decision to the issue. |
Summary
Some LLM clients incorrectly serialize booleans as strings (
"false"instead offalsein JSON). This causes FastMCP tools to receivethe string
'false'instead of PythonFalse, makingif param:checks evaluate incorrectly.Root Cause
The existing
pre_parse_jsonmethod was supposed to handle this viajson.loads, but boolean values were being skipped because:json.loads("false")correctly returns PythonFalseboolis a subclass ofintin Pythonisinstance(pre_parsed, str | int | float)matches booleans (sinceisinstance(False, int)isTrue)Changes
pre_parse_jsonmethod"true"/"false"strings (any case) → PythonTrue/FalseboolTesting
Related Issues
Fixes #1843